package com.andnnl.redis;

import cn.hutool.json.JSONUtil;
import com.andnnl.config.ConfigFileHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import redis.clients.jedis.commands.JedisCommands;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * 支持集群模式
 */
public class RedisSession290 implements IRedisService {
    private static final Logger logger = LoggerFactory.getLogger(RedisSession290.class);

    //redis server config
    private static String serverIp;
    private static int port;
    private static int portRead;
    private static final int maxActive = 2000;
    private static String clusterStr;
    private static final boolean useCluster;
    private static final int maxIdle = 10;
    private static final int maxWait = 5000;
    private static final boolean testOnBorrow = true;
    private static final boolean testOnReturn = true;
    private static JedisPool pool = null;
    private static JedisPool poolRead = null;
    //    private static JedisCluster jedisCluster = null;
    private static RedisSession290 redisSessionCluster = null;//集群的会话只有一份

    static {
        ConfigFileHelper c = new ConfigFileHelper("config");
        String ruc = c.getString("redis.use.cluster");
        useCluster = ruc == null || "".equals(ruc) ? false : Boolean.parseBoolean(ruc);
        logger.info("redis.use.cluster={}", useCluster);
        if (useCluster) {
            clusterStr = c.getString("redis.cluster");
            logger.info("redis.cluster={}", clusterStr);
        } else {
            serverIp = c.getString("redis.ip");
            port = c.getInt("redis.port", 6379);
            portRead = c.getInt("redis.port.read", 6380);
            logger.info("redis.ip={}", serverIp);
            logger.info("redis.port={}", port);
            logger.info("redis.port.read={}", portRead);
        }
    }

    private JedisCommands session = null;
    private boolean isRead = false;

    private RedisSession290(JedisCommands session) {
        this.session = session;
    }

    private RedisSession290(JedisCommands session, boolean isRead) {
        this.session = session;
        this.isRead = isRead;
    }

    /***
     * 获取JedisPool对象.
     * zzz
     *
     * @return
     */
    public synchronized static RedisSession290 getRedisClient() {

        if (useCluster) {
            if (redisSessionCluster != null)
                return redisSessionCluster;
            return initCluster();
        } else {
            if (null == pool) {
                JedisPoolConfig config = getJedisPoolConfig();
                pool = new JedisPool(config, serverIp, port);
            }
            return new RedisSession290(pool.getResource());//每次返回一个新的RedisSession和JRedis对象
        }
    }

    /**
     * 只读库
     * 加synchronized解决java.lang.IllegalStateException: Invalidated object not currently part of this pool
     *
     * @return
     */
    public synchronized static RedisSession290 getRedisClientRead() {
        if (useCluster) {
            if (redisSessionCluster != null)
                return redisSessionCluster;
            return initCluster();
        } else {
            if (null == poolRead) {
                JedisPoolConfig config = getJedisPoolConfig();
                poolRead = new JedisPool(config, serverIp, portRead);
            }
            return new RedisSession290(poolRead.getResource(), true);//每次返回一个新的RedisSession和JRedis对象
        }
    }

    private static synchronized RedisSession290 initCluster() {
        if (redisSessionCluster != null)
            return redisSessionCluster;
        JedisPoolConfig config = getJedisPoolConfig();
        Set<HostAndPort> hostAndPortSet = new HashSet();
        String[] csSt = clusterStr.split(",");
        for (String ipSt : csSt) {
            String[] ss = ipSt.split(":");
            hostAndPortSet.add(new HostAndPort(ss[0], Integer.parseInt(ss[1])));
        }
        JedisCluster jc = new JedisCluster(hostAndPortSet, config);
//        JedisCluster jc = new JedisCluster(hostAndPortSet);
//        redisSessionCluster = new RedisSession290(jc);
        return redisSessionCluster;
    }

    private static JedisPoolConfig getJedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        //高版本
        //连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
        config.setBlockWhenExhausted(true);
        //最大连接数
        config.setMaxTotal(maxActive);
        //最大空闲连接数
        config.setMaxIdle(maxIdle);
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
//        config.setMaxWaitMillis(maxWait);
        config.setMaxWaitMillis(-1);
        //在获取连接的时候检查有效性, 默认false
        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);
        return config;
    }


    /***
     * 根据key 获取String值
     *
     * @param key
     * @return
     */
    @Override
    public String get(String key) {
        String result = null;
        if (null != key) {
            result = session.get(key);

        }
        return result;
    }

    /***
     * 给key设置对象值
     *
     * @param key
     * @param value 只能是String和Map类型
     */
    @Override
    public String set(String key, Object value) {
        return set(key, value, null);
    }

    /***
     * 给key 设置对象值，并给予过期时间
     *
     * @param key
     * @param value
     * @param seconds
     */
    @Override
    public String set(String key, Object value, Integer seconds) {
        if (null != key) {
            if (null != value) {
                if (value instanceof String) {
                    String o = (String) value;
                    if (null == seconds) {
                        return session.set(key, o);
                    } else {
                        return session.setex(key, seconds, o);
                    }
                } else if (value instanceof Map) {
                    Map<String, String> valueMap = (Map<String, String>) value;
                    return session.hmset(key, valueMap);
                } else {
//                    new RuntimeException("redis set value must be a String or Map");
                    String o = value.toString();
                    if (null == seconds) {
                        return session.set(key, o);
                    } else {
                        return session.setex(key, seconds, o);
                    }
                }
            }

        }
        return "";
    }

    /***
     * 判断key是否存在
     * @param key
     * @return
     */
    @Override
    public boolean existsKey(String key) {
        if (null != key && session.exists(key))
            return true;
        return false;
    }

    /***
     *删除key
     *
     * @param key
     * @return
     */
    @Override
    public Long del(String key) {
        if (null != key && session.exists(key)) {
            return session.del(key);
        }
        return (long) -1;
    }

    public JedisCommands getSession() {
        return session;
    }

    /***
     * 释放redis对象
     */
    @Override
    public void destroy() {
        if (!useCluster) {
            if (this.session != null && this.session instanceof Jedis)
                ((Jedis) this.session).close();
        }
    }

    @Override
    public void lpush(String key, String val) {

        session.lpush(key, val);
    }

    @Override
    public void expire(String key, int seconds) {

        session.expire(key, seconds);
    }

    @Override
    public List lrange(String key, int start, int stop) {

        return session.lrange(key, start, stop);
    }

    @Override
    public Long hset(String key, String field, String value) {
        return this.session.hset(key, field, value);
    }

    @Override
    public String hget(String key, String field) {
        return this.session.hget(key, field);
    }

    @Override
    public Map<String, String> hgetAll(final String key) {
        return this.session.hgetAll(key);
    }

    @Override
    public long incr(String key) {
        return this.session.incr(key);
    }

    @Override
    public long incrBy(String key, long field) {
        return this.session.incrBy(key, field);
    }

    @Override
    public long decr(String key) {
        return this.session.decr(key);
    }

    @Override
    public long decrBy(String key, long field) {
        return this.session.decrBy(key, field);
    }

    /**
     * 以秒为单位，返回给定 key 的剩余生存时间，
     *
     * @param key
     * @return 当 key 不存在时，返回 -2 。 当 key 存在但没有设置剩余生存时间时，返回 -1 。
     * 否则，以秒为单位，返回 key 的剩余生存时间。
     */
    @Override
    public long ttl(String key) {
        return this.session.ttl(key);
    }

    @Override
    public Long publish(String key, String value) {
        return null;
    }

    @Override
    public Long publish(byte[] key, byte[] value) {
        return 0L;
    }

    @Override
    public void subscribe(JedisPubSub jedisPubSub, String... channels) {

    }

    @Override
    public void subscribe(BinaryJedisPubSub jedisPubSub, byte[]... channels) {

    }

    @Override
    public void psubscribe(JedisPubSub jedisPubSub, String... patterns) {

    }

    @Override
    public void psubscribe(BinaryJedisPubSub jedisPubSub, byte[]... patterns) {

    }

//    /**
//     * RedisSession类的任意方法调用用try包装
//     * @param c 任意方法调用
//     * @return 返回true，false，表示操作是否成功
//     */
//    public static boolean operateTF(Consumer<RedisSession> c) {
//        RedisSession redis = null;
//        try {
//            redis = getRedisClient();
//            if (redis == null) {
//                return false;
//            }
//            c.accept(redis);
//            return true;
//        } catch (Exception e) {
//            logger.error(e.getMessage(), e);
//        } finally {
//            if (redis != null)
//                redis.destroy();
//        }
//        return false;
//    }

    /**
     * RedisSession类的任意方法调用用try包装，并返回值
     * 用法 ：String rsStr=RedisSession.operate(s->s.set("asdf","asdf"));
     *
     * @param c 任意方法调用
     * @return 任意方法的返回值，返回Null，表示操作失败
     */
    public static <R> R operate(Function<RedisSession290, R> c) {
        RedisSession290 redis = null;
        try {
            redis = getRedisClient();
            if (redis == null) {
                return null;
            }
            return c.apply(redis);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (redis != null)
                redis.destroy();
        }
        return null;
    }

    /**
     * 批量操作，RedisSession类的任意方法调用用try包装，并返回值
     * 如果部分成功也会返回List
     *
     * @param list 列表
     * @param c    任意方法调用
     * @return 任意方法的返回值，返回Null，表示操作失败
     */
    public static <U, R> List<R> operate(List<U> list, BiFunction<RedisSession290, U, R> c) {
        RedisSession290 redis = null;
        List<R> result = new ArrayList<>();
        try {
            redis = getRedisClient();
            if (redis == null) {
                return null;
            }
            for (U u : list) {
                result.add(c.apply(redis, u));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            if (redis != null)
                redis.destroy();
        }
        return result;
    }

    public static void main(String[] args) throws InterruptedException {
        RedisSession290 r = RedisSession290.getRedisClient();
        //logger.info(r.get("sms25"));

        for (int i = 0; i < 4; i++) {
            String key = "key" + i;
            r.set(key, "test-" + i);
            logger.info(key + "-->" + r.get(key));
        }

        r.destroy();
        r.test();

    }

    public void test() {
        String rsStr = RedisSession290.operate(s -> s.set("asdf", "asdf"));
        logger.info(rsStr);

        Long rs = RedisSession290.operate(s -> s.del("asdf"));
        logger.info("" + rs);

        List<String> list = Arrays.asList("asdf1", "asdf2", "asdf3");
        List<String> rsStrList = RedisSession290.operate(list, (s, b) -> s.set(b, b));
        logger.info(JSONUtil.toJsonStr(rsStrList));

        List<String> list2 = Arrays.asList("asdf1", "asdf2", "asdf3");
        List<Long> rsStrList2 = RedisSession290.operate(list2, (s, b) -> s.del(b));
        logger.info(JSONUtil.toJsonStr(rsStrList2));

        //session.del("list");
        session.lpush("27767529587889671719922901515223", "abc");
        session.expire("27767529587889671719922901515223", 15);
        session.lpush("27767529587889671719922901515223", "xzc");
        session.expire("27767529587889671719922901515223", 15);
        session.lpush("27767529587889671719922901515223", "erf");
        session.expire("27767529587889671719922901515223", 15);
        session.lpush("27767529587889671719922901515223", "bnh");
        session.expire("27767529587889671719922901515223", 15);
        //LRANGE key start stop返回列表key中指定区间内的元素，区间以偏移量start和stop指定。下标(index)参数start和stop都以0为底，也就是说，以0表示列表的第一个元素，以1表示列表的第二个元素，以此类推。你也可以使用负数下标，以-1表示列表的最后一个元素，-2表示列表的倒数第二个元素，以此类推。
        List<String> listx = session.lrange("27767529587889671719922901515223", 0, -1);
        for (int i = 0; i < listx.size(); i++) {
            logger.info("" + listx.get(i));
        }
    }

}
